package com.jinzhi.oss.utils;

import com.alibaba.fastjson.JSON;
import com.jinzhi.common.utils.DateUtils;
import com.jinzhi.common.utils.Result;
import com.jinzhi.oss.config.MinioClientConfig;
import com.jinzhi.oss.domain.MinioProperty;
import io.minio.*;

import io.minio.errors.*;
import io.minio.http.Method;
import io.minio.messages.Item;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.SequenceInputStream;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.text.DecimalFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;

@Slf4j
@Component
public class MinioUtil {

    @Autowired
    private MinioProperty minioProperty;

    /**
     * Minio文件上传
     *
     * @param file       文件实体
     * @param fileName   文件名（如果是自定义文件名，需包含日期的文件夹 例：2023/12/20/1.jpg）
     * @param bucketName 所存文件夹（桶名）
     * @return
     */
    public Result<String> minioUpload(MultipartFile file, String fileName, String bucketName) {
        if(StringUtils.isBlank(bucketName)){
            bucketName = minioProperty.getBucketName();
        }
        String filePath = null;
        try {
            MinioClient minioClient = MinioClientConfig.getMinioClient();
            // fileName为空，说明要使用源文件名上传
            if (fileName == null) {
                fileName = file.getOriginalFilename();
            }
            fileName = fileName.replaceAll(" ", "_");
            log.info("========当前上传的文件:"+fileName+"，存储桶名称:"+bucketName+"========");
            InputStream inputStream = file.getInputStream();
            PutObjectArgs objectArgs = PutObjectArgs.builder().bucket(bucketName).object(fileName)
                    .stream(inputStream, file.getSize(), -1).contentType(file.getContentType()).build();
            //文件名称相同会覆盖
            minioClient.putObject(objectArgs);
            filePath = minioProperty.getEndpoint() + "/" + bucketName + "/" + fileName;
            return Result.ok(filePath);
        } catch (Exception e) {
            e.printStackTrace();
            return Result.fail("上传失败！");
        }
    }


    /**
     * 检查存储桶是否存在
     *
     * @param bucketName 存储桶名称
     * @return
     */
    public boolean bucketExists(String bucketName) {
        boolean flag = false;
        try {
            flag = MinioClientConfig.bucketExists(bucketName);
            if (flag) {
                return true;
            }
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return false;
    }

    /**
     * 获取文件流
     *
     * @param fileName 文件名
     * @param bucketName 桶名（文件夹）
     * @return
     */
    public InputStream getFileInputStream(String bucketName,String fileName) {
        try {
            MinioClient minioClient = MinioClientConfig.getMinioClient();
            return minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(fileName).build());
        } catch (Exception e) {
            e.printStackTrace();
            log.error(e.getMessage());
        }
        return null;
    }


    /**
     * @param bucketName:
     * @author
     * @description: 创建桶
     * @date 2022/8/16 14:36
     */
    public void createBucketName(String bucketName) {
        try {
            if (StringUtils.isBlank(bucketName)) {
                return;
            }
            MinioClient minioClient = MinioClientConfig.getMinioClient();
            boolean isExist = MinioClientConfig.bucketExists(bucketName);
            if (isExist) {
                log.info("Bucket {} already exists.", bucketName);
            } else {
                minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
            }
        } catch (Exception e) {
            e.printStackTrace();
            log.error(e.getMessage());
        }
    }
    /**
     * 下载文件
     *
     * @param originalName 文件路径
     */
    public InputStream downloadFile(String bucketName, String originalName, HttpServletResponse response) {
        if(StringUtils.isBlank(bucketName)){
            bucketName = minioProperty.getBucketName();
        }
        try {
            MinioClient minioClient = MinioClientConfig.getMinioClient();
            InputStream file = minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(originalName).build());
            String filename = new String(originalName.getBytes("ISO8859-1"), StandardCharsets.UTF_8);
            if (StringUtils.isNotBlank(originalName)) {
                filename = originalName;
            }
            // 设置强制下载不打开
            // res.setContentType("application/force-download");
            response.setHeader("Content-Disposition", "attachment;filename=" + filename);
            ServletOutputStream servletOutputStream = response.getOutputStream();
            int len;
            byte[] buffer = new byte[1024];
            while ((len = file.read(buffer)) > 0) {
                servletOutputStream.write(buffer, 0, len);
            }
            servletOutputStream.flush();
            file.close();
            servletOutputStream.close();
            return file;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 删除
     * @param fileName
     * @return
     * @throws Exception
     */
    public boolean deleteFile(String bucketName,String fileName){
        if(StringUtils.isBlank(bucketName)){
            bucketName = minioProperty.getBucketName();
        }
        try {
            MinioClient minioClient = MinioClientConfig.getMinioClient();
            minioClient.removeObject( RemoveObjectArgs.builder().bucket(bucketName).object(fileName).build());
        }catch (Exception e){
            return false;
        }
        return true;
    }

    /**
     * @param bucketName:
     * @description: 删除桶
     * @date 2022/8/16 14:36
     */
    public void deleteBucketName(String bucketName) {
        try {
            if (StringUtils.isBlank(bucketName)) {
                return;
            }
            MinioClient minioClient = MinioClientConfig.getMinioClient();
            boolean isExist = MinioClientConfig.bucketExists(bucketName);
            if (isExist) {
                minioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build());
            }
        } catch (Exception e) {
            e.printStackTrace();
            log.error(e.getMessage());
        }
    }
    /**
     * @param bucketName:
     * @description: 删除桶下面所有文件
     * @date 2022/8/16 14:36
     */
    public void deleteBucketFile(String bucketName){
        try {
            if (StringUtils.isBlank(bucketName)) {
                return;
            }
            MinioClient minioClient = MinioClientConfig.getMinioClient();
            boolean isExist = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
            if (isExist) {
                // 获取桶下的所有文件
                Iterable<io.minio.Result<Item>> results = minioClient.listObjects(
                        ListObjectsArgs.builder().bucket(bucketName).build()
                );

                // 循环删除
                results.forEach(result -> {
                    try {
                        String objectName = result.get().objectName();
                        deleteFile(bucketName,objectName);
                    } catch (ErrorResponseException e) {
                        throw new RuntimeException(e);
                    } catch (InsufficientDataException e) {
                        throw new RuntimeException(e);
                    } catch (InternalException e) {
                        throw new RuntimeException(e);
                    } catch (InvalidKeyException e) {
                        throw new RuntimeException(e);
                    } catch (InvalidResponseException e) {
                        throw new RuntimeException(e);
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    } catch (NoSuchAlgorithmException e) {
                        throw new RuntimeException(e);
                    } catch (ServerException e) {
                        throw new RuntimeException(e);
                    } catch (XmlParserException e) {
                        throw new RuntimeException(e);
                    }
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
            log.error(e.getMessage());
        }
    }

    /**
     * 根据文件路径得到预览文件绝对地址
     *
     * @param bucketName
     * @param fileName
     * @return
     */
    public String getPreviewFileUrl(String bucketName, String fileName) {
        if(StringUtils.isBlank(bucketName)){
            bucketName = minioProperty.getBucketName();
        }
        // 查看文件地址
        MinioClient minioClient = MinioClientConfig.getMinioClient();
        GetPresignedObjectUrlArgs build = new GetPresignedObjectUrlArgs().builder().bucket(bucketName).object(fileName).method(Method.GET).build();
        try {
            String url = minioClient.getPresignedObjectUrl(build);
            return url;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 去除fileName中的endpoint、bucketName
     * @return
     */
    public String getFileName(String bucketName, String fileName){
        String newName = null;
        if (StringUtils.isBlank(bucketName)) {
            bucketName = minioProperty.getBucketName();
        }
        newName = fileName.replaceAll(minioProperty.getEndpoint()+"/"+bucketName,"");
        return newName;
    }


    /**
     * 判断文件是否存在
     *
     * @param bucketName 桶名称
     * @param fileName 文件名称, 如果要带文件夹请用 / 分割, 例如 /help/index.html
     * @return true存在, 反之
     */
    public Boolean checkFileIsExist(String bucketName, String fileName) {
        try {
            MinioClient minioClient = MinioClientConfig.getMinioClient();
            minioClient.statObject(
                    StatObjectArgs.builder().bucket(bucketName).object(fileName).build()
            );
        } catch (Exception e) {
            return false;
        }
        return true;
    }



    /**
     * 将切片合并到新桶   块文件必须满足 名字是 0、1、2、3、5....
     * @param bucketName   存块文件的桶
     * @param bucketName1  存新文件的桶
     * @param fileName    存到新桶中的文件名称
     * @return
     */
    public boolean merge(String bucketName,String bucketName1,String fileName)  {
        try {
            MinioClient minioClient = MinioClientConfig.getMinioClient();
            List<ComposeSource> sourceObjectList = new ArrayList<>();
            List<String> fileNames = this.getFolderList(bucketName);


            if (!fileNames.isEmpty()) {
                Collections.sort(fileNames, new Comparator<String>() {
                    @Override
                    public int compare(String o1, String o2) {
                        if (Integer.parseInt(o2) > Integer.parseInt(o1)) {
                            return -1;
                        }
                        return 1;
                    }
                });

                for (String name : fileNames) {
                    sourceObjectList.add(ComposeSource.builder().bucket(bucketName).object(name).build());
                }
            }

            minioClient.composeObject(
                    ComposeObjectArgs.builder()
                            .bucket(bucketName1)
                            .object(fileName)
                            .sources(sourceObjectList)
                            .build());
            return true;
        } catch (Exception e) {
            //失败就删除桶和切片文件
            this.deleteBucketFile(bucketName);
            this.deleteBucketName(bucketName);
        }
        return false;
    }


    /**
     * 获取桶下面的所有文件
     * @param bucketName
     * @return
     * @throws Exception
     */
    public List<String> getFolderList(String bucketName) throws Exception{
        MinioClient minioClient = MinioClientConfig.getMinioClient();
        Iterable<io.minio.Result<Item>> results = minioClient.listObjects(
                ListObjectsArgs.builder().bucket(bucketName).build()
        );
        Iterator<io.minio.Result<Item>> iterator = results.iterator();
        List<String> items = new ArrayList<>();
//        String format = "{'fileName':'%s','fileSize':'%s'}";
        while(iterator.hasNext()) {
            Item item = iterator.next().get();
            log.info("文件名：" + item.objectName() + "，大小：" + item.size());
//            items.add(JSON.parse((String.format(format, item.objectName(),
//                    formatFileSize(item.size())))));
            items.add(item.objectName());
        }
        return items;
    }


    /**
     * 大小单位转换
     * @param fileS
     * @return
     */
    private static String formatFileSize(long fileS) {
        DecimalFormat df = new DecimalFormat("#.00");
        String fileSizeString = "";
        String wrongSize = "0B";
        if (fileS == 0) {
            return wrongSize;
        }
        if (fileS < 1024) {
            fileSizeString = df.format((double) fileS) + " B";
        } else if (fileS < 1048576) {
            fileSizeString = df.format((double) fileS / 1024) + " KB";
        } else if (fileS < 1073741824) {
            fileSizeString = df.format((double) fileS / 1048576) + " MB";
        } else {
            fileSizeString = df.format((double) fileS / 1073741824) + " GB";
        }
        return fileSizeString;
    }

}
